<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>精美桌球游戏</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: 'Segoe UI', sans-serif;
    }

    body {
      background: linear-gradient(135deg, #1a1a1a, #333);
      min-height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 20px;
      transition: background 0.3s ease;
    }

    #game-container {
      position: relative;
      background: linear-gradient(135deg, #084228, #0a5c36);
      border-radius: 22px;
      padding: 22px;
      box-shadow: 0 0 44px rgba(0, 0, 0, 0.7), 
                  inset 0 0 22px rgba(0, 0, 0, 0.3),
                  0 0 20px rgba(255, 215, 0, 0.2);
      max-width: 926px;
      width: 100%;
      margin-top: 44px;
      animation: fadeIn 0.5s ease-out;
    }
    canvas { 
      border-radius: 12px;
      box-shadow: 0 0 32px rgba(0, 0, 0, 0.6),
                  inset 0 0 16px rgba(0, 0, 0, 0.2),
                  0 0 15px rgba(255, 215, 0, 0.1);
      background: linear-gradient(180deg, #0b6c40, #063822);
      margin-bottom: 44px;
    }

    #game-info {
      position: absolute;
      top: -70px;
      width: calc(100% - 44px);
      display: flex;
      justify-content: space-between;
      align-items: center;
      background: rgba(34, 34, 34, 0.95);
      backdrop-filter: blur(5px);
      padding: 14px 22px;
      border-radius: 30px;
      box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
      border: 1px solid rgba(255, 215, 0, 0.2);
    }

    #instructions {
      font-size: 18px;
      color: #ffd700;
      font-style: italic;
      text-shadow: 0 0 8px #ffd700;
    }

    #scoreboard, #timer {
      font-size: 20px;
      color: #fff;
      font-weight: 500;
      display: flex;
      align-items: center;
      gap: 8px;
    }

    #scoreboard span, #timer span {
      color: #48d1cc;
      font-size: 24px;
      font-weight: 700;
    }

    #power-meter {
      position: absolute;
      bottom: -50px;
      left: 50%;
      transform: translateX(-50%);
      width: 80%;
      max-width: 400px;
      height: 10px;
      background: #1a1a1a;
      border-radius: 50px;
      box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.5);
    }

    #power-level {
      height: 100%;
      background: linear-gradient(90deg, #0f0, #ff0, #f00);
      border-radius: 40px;
      transition: width 0.1s linear;
      box-shadow: 0 0 12px rgba(0, 255, 0, 0.3),
                  0 0 12px rgba(255, 255, 0, 0.3),
                  0 0 12px rgba(255, 0, 0, 0.3);
    }

    .tooltip {
      position: absolute;
      background: rgba(34, 34, 34, 0.9);
      color: #fff;
      padding: 6px 12px;
      border-radius: 20px;
      font-size: 14px;
      opacity: 0;
      transition: opacity 0.3s ease;
      pointer-events: none;
    }

    #player-turn {
      position: absolute;
      bottom: -90px;
      left: 50%;
      transform: translateX(-50%);
      color: white;
      font-size: 18px;
      background: rgba(34, 34, 34, 0.9);
      padding: 8px 16px;
      border-radius: 20px;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
    }

    .player-indicator {
      display: inline-block;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      margin-right: 8px;
    }

    .solid-color { background-color: #FFD700; }
    .striped-color { background-color: #0000FF; }

    /* 添加进球动画样式 */
    @keyframes ballPocketed {
        0% { transform: scale(1); opacity: 1; }
        50% { transform: scale(0.5); opacity: 0.5; }
        100% { transform: scale(0); opacity: 0; }
    }

    @keyframes veryGood {
        0% { transform: scale(0); opacity: 0; }
        50% { transform: scale(1.2); opacity: 1; }
        100% { transform: scale(1); opacity: 0; }
    }

    .very-good {
        position: absolute;
        color: #ffd700;
        font-size: 24px;
        font-weight: bold;
        text-shadow: 0 0 10px #ffd700;
        animation: veryGood 1s ease-out;
        pointer-events: none;
    }

    #pocketed-balls-display {
        position: absolute;
        top: 20px;
        width: 100%;
        display: flex;
        justify-content: space-between;
        padding: 0 20px;
        color: white;
        font-size: 16px;
        text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
    }

    #pocketed-balls-display div {
        background: rgba(0, 0, 0, 0.5);
        padding: 5px 10px;
        border-radius: 5px;
        border: 1px solid rgba(255, 255, 255, 0.2);
    }

    .side-pocket {
        position: absolute;
        top: 50%;
        width: 120px;
        min-height: 220px;
        background: rgba(0,0,0,0.85);
        border-radius: 18px;
        border: 3px solid rgba(255,255,255,0.4);
        box-shadow: 0 4px 32px rgba(0,0,0,0.25);
        padding: 18px 10px 10px 10px;
        transform: translateY(-50%);
        display: flex;
        flex-direction: column;
        align-items: center;
        z-index: 20;
    }
    .side-pocket.left {
        left: -135px;
    }
    .side-pocket.right {
        right: -135px;
    }
    .side-pocket h3 {
        margin: 0 0 12px 0;
        font-size: 18px;
        color: #ffd700;
        text-align: center;
    }
    .side-pocket .ball-list {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        justify-content: center;
    }
    .side-pocket .ball-item {
        width: 28px;
        height: 28px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 15px;
        color: white;
        font-weight: bold;
        text-shadow: 0 0 2px black;
    }

    /* 添加新的样式 */
    .ball-item {
        width: 28px;
        height: 28px;
        border-radius: 50%;
        position: relative;
        overflow: hidden;
        box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.3);
        border: 1px solid rgba(255, 255, 255, 0.2);
    }
    
    .ball-inner {
        width: 100%;
        height: 100%;
        position: relative;
        display: flex;
        align-items: center;
        justify-content: center;
    }
    
    .ball-stripe {
        position: absolute;
        width: 100%;
        height: 30%; /* 调整条纹高度 */
        background-color: white;
        top: 35%; /* 调整条纹垂直位置 */
        left: 0;
    }
    
    .ball-number {
        width: 40%; /* 调整数字背景大小 */
        height: 40%; /* 调整数字背景大小 */
        background-color: white;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 12px;
        font-weight: bold;
        color: black;
        border: 1px solid rgba(0, 0, 0, 0.3);
        z-index: 1;
    }
  </style>
<!-- Code injected by Five-server -->
  <script async data-id="five-server" data-file="c:\Users\PSS\Desktop\hw14\模板1.html" type="application/javascript" src="/fiveserver.js"></script>
  </head>
<body>
  <div id="game-container">
    <div id="player1-pocket" class="side-pocket left"></div>
    <div id="player2-pocket" class="side-pocket right"></div>
    <div id="game-info">
      <div id="instructions">
        按住鼠标左键蓄力，松开击球<br>
        方向由鼠标位置决定
      </div>
      <div id="scoreboard">得分：<span id="score">0</span></div>
      <div id="timer">用时：<span id="time">0:00</span></div>
    </div>
    <canvas id="pool-table" width="880" height="440"></canvas>
    <div id="power-meter">
      <div id="power-level"></div>
    </div>
    <div class="tooltip" id="power-tooltip">蓄力强度：<span>0%</span></div>
    <div id="player-turn">
      当前玩家：<span class="player-indicator solid-color"></span>全色球
    </div>
  </div>

  <script>
    // 游戏常量（关键参数放大10%）
    var FRICTION = 0.98;
    var MIN_VELOCITY = 0.1;
    var BALL_RADIUS = 16.5; // 原15px ×1.1
    var POCKET_RADIUS = 23.1; // 原21px ×1.1

    // 获取Canvas和上下文
    var canvas = document.getElementById('pool-table');
    var ctx = canvas.getContext('2d');
    var powerLevel = document.getElementById('power-level');
    var scoreDisplay = document.getElementById('score');
    var timeDisplay = document.getElementById('time');
    var playerTurn = document.getElementById('player-turn');

    // 鼠标位置变量
    var mouseX = 0;
    var mouseY = 0;
    var isMouseInCanvas = false;

    // 游戏状态
    var balls = [];
    var pockets = [];
    var cueBall = null;
    var isCharging = false;
    var chargePower = 0;
    var lastTime = 0;
    var gameRunning = false;
    var whiteBallInPocket = false;
    var score = 0;
    var startTime = 0;
    var isTimerRunning = false;
    var blackBallScoreCondition = false;
    var currentPlayer = 'solid'; // 'solid' 或 'striped'
    var playerAssigned = false;
    var gameStarted = false;
    var ballsPocketedThisTurn = [];
    var hasHitAnyBall = false; // 记录是否击中任何球
    var hasHitOwnBall = false; // 记录是否击中自己的球
    var foul = false; // 记录是否犯规

    // 添加玩家系统
    const gameState = {
        currentPlayer: 1,
        player1Type: null, // 'solid' 或 'striped'
        player2Type: null,
        player1Score: 0,
        player2Score: 0,
        firstBallType: null, // 记录第一个进球的类型
        gamePhase: 'open', // 'open', 'assigned', 'end'
        foul: false,
        consecutiveHits: 0,
        player1PocketedBalls: [], // 记录玩家1打进的球
        player2PocketedBalls: [], // 记录玩家2打进的球
        canPlaceWhiteBall: false, // 是否可以将白球放在任意位置
        hasHitOwnBall: false, // 是否击中自己的球
        hasHitAnyBall: false // 是否击中任何球
    };

    // 球袋位置
    function createPockets() {
      var offset = 10; // 原20px ×1.1
      return [
        { x: offset, y: offset },
        { x: canvas.width / 2, y: offset-10 },
        { x: canvas.width - offset, y: offset},
        { x: offset, y: canvas.height - offset },
        { x: canvas.width / 2, y: canvas.height - offset+10 },
        { x: canvas.width - offset, y: canvas.height - offset }
      ];
    }

    // 球类定义
    function Ball(x, y, color, number, isCue, ballType) {
      this.x = x;
      this.y = y;
      this.radius = BALL_RADIUS;
      this.color = color;
      this.number = number;
      this.vx = 0;
      this.vy = 0;
      this.isCue = isCue || false;
      this.inPocket = false;
      this.ballType = ballType || null; // 'solid', 'striped', 'black'
    }

    Ball.prototype.draw = function() {
      if (this.inPocket) return;

      // 绘制球体基础
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
      ctx.fillStyle = this.color;
      ctx.fill();

      // 绘制球的边框
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
      ctx.strokeStyle = 'rgba(0, 0, 0, 0.3)';
      ctx.lineWidth = 1.5;
      ctx.stroke();

      // 如果是花色球，绘制上下白色部分和中间彩色条纹
      if (this.ballType === 'striped' && !this.isCue) {
        ctx.save();
        
        // 绘制上白色部分（更小）
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius * 0.9, -Math.PI/2, Math.PI/2);
        ctx.fillStyle = 'white';
        ctx.fill();
        
        // 绘制下白色部分（更小）
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius * 0.9, Math.PI/2, -Math.PI/2);
        ctx.fillStyle = 'white';
        ctx.fill();
        
        // 绘制中间彩色条纹（更宽）
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius * 0.9, 0, Math.PI * 2);
        ctx.clip();
        ctx.fillStyle = this.color;
        ctx.fillRect(this.x - this.radius, this.y - this.radius * 0.5, 
                    this.radius * 2, this.radius * 1.0);
        
        ctx.restore();
      }

      // 绘制球上的数字（不包括白球）
      if (!this.isCue) {
        // 绘制数字背景
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius * 0.5, 0, Math.PI * 2);
        ctx.fillStyle = 'white';
        ctx.fill();

        // 绘制数字背景边框
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius * 0.5, 0, Math.PI * 2);
        ctx.strokeStyle = 'rgba(0, 0, 0, 0.3)';
        ctx.lineWidth = 1;
        ctx.stroke();

        // 绘制数字
        ctx.font = 'bold ' + (this.radius * 0.7) + 'px Arial';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillStyle = 'black';
        ctx.fillText(this.number.toString(), this.x, this.y);
      }
    };

    // 辅助函数：调整颜色亮度
    function lightenColor(color, percent) {
      var num = parseInt(color.replace("#", ""), 16);
      var amt = Math.round(2.55 * percent);
      var R = (num >> 16) + amt;
      var G = ((num >> 8) & 0x00FF) + amt;
      var B = (num & 0x0000FF) + amt;
      
      return "#" + (
        0x1000000 + 
        (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 + 
        (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 + 
        (B < 255 ? B < 1 ? 0 : B : 255)
      ).toString(16).slice(1);
    }

    Ball.prototype.update = function() {
      if (this.inPocket) return;
      
      // 应用摩擦力
      this.vx *= FRICTION;
      this.vy *= FRICTION;
      
      // 当速度足够小时停止
      if (Math.abs(this.vx) < MIN_VELOCITY && Math.abs(this.vy) < MIN_VELOCITY) {
        this.vx = 0;
        this.vy = 0;
      }
      
      // 更新位置
      this.x += this.vx;
      this.y += this.vy;

      // 边界检测和反弹
      if (this.x - this.radius < 0) {
        this.x = this.radius;
        this.vx = -this.vx * 0.9; // 能量损失
      } else if (this.x + this.radius > canvas.width) {
        this.x = canvas.width - this.radius;
        this.vx = -this.vx * 0.9;
      }

      if (this.y - this.radius < 0) {
        this.y = this.radius;
        this.vy = -this.vy * 0.9;
      } else if (this.y + this.radius > canvas.height) {
        this.y = canvas.height - this.radius;
        this.vy = -this.vy * 0.9;
      }

      // 检测是否落袋
      for (var i = 0; i < pockets.length; i++) {
        var pocket = pockets[i];
        var dx = this.x - pocket.x;
        var dy = this.y - pocket.y;
        var distance = Math.sqrt(dx * dx + dy * dy);
        
        if (distance < POCKET_RADIUS && !this.inPocket) {
          this.inPocket = true;
          
          // 添加进球动画
          const ballElement = document.createElement('div');
          ballElement.style.position = 'absolute';
          ballElement.style.left = this.x + 'px';
          ballElement.style.top = this.y + 'px';
          ballElement.style.width = this.radius * 2 + 'px';
          ballElement.style.height = this.radius * 2 + 'px';
          ballElement.style.borderRadius = '50%';
          ballElement.style.backgroundColor = this.color;
          ballElement.style.animation = 'ballPocketed 0.5s ease-out';
          document.getElementById('game-container').appendChild(ballElement);

          // 只有非白球才显示Very Good
          if (!this.isCue) {
            const veryGoodElement = document.createElement('div');
            veryGoodElement.className = 'very-good';
            veryGoodElement.textContent = 'Very Good!';
            veryGoodElement.style.left = this.x + 'px';
            veryGoodElement.style.top = (this.y - 30) + 'px';
            document.getElementById('game-container').appendChild(veryGoodElement);
            setTimeout(() => veryGoodElement.remove(), 1000);
          }

          // 移除动画元素
          setTimeout(() => ballElement.remove(), 1000);

          if (!this.isCue) {
            if (this.number === 8) {
              // 黑球处理逻辑
              if (gameState.gamePhase === 'assigned') {
                const currentPlayerType = gameState.currentPlayer === 1 ? 
                    gameState.player1Type : gameState.player2Type;
                
                // 检查是否已清完己方球组
                const playerBallsRemaining = balls.filter(b => 
                    !b.inPocket && b.ballType === currentPlayerType && b.number !== 8
                ).length === 0;
                
                if (playerBallsRemaining) {
                  // 当前玩家打进黑球，获胜
                  handleGameWin(gameState.currentPlayer);
                } else {
                  // 当前玩家打进黑球，失败
                  handleGameLose(gameState.currentPlayer);
                }
              } else {
                // 开球阶段打进黑球，重新开始
                alert('开球阶段打进黑球，重新开始！');
                initGame();
              }
            } else {
              // 普通球处理逻辑
              if (gameState.gamePhase === 'open') {
                // 开球阶段，确定玩家类型
                gameState.firstBallType = this.ballType;
                gameState.player1Type = this.ballType;
                gameState.player2Type = this.ballType === 'solid' ? 'striped' : 'solid';
                gameState.gamePhase = 'assigned';
                // 记录第一个进球的玩家
                gameState.player1PocketedBalls.push(this.number);
                updatePlayerDisplay();
              } else {
                // 已分配玩家类型阶段
                const currentPlayerType = gameState.currentPlayer === 1 ? 
                    gameState.player1Type : gameState.player2Type;
                
                if (this.ballType === currentPlayerType) {
                  // 打进正确类型的球，继续击球
                  if (gameState.currentPlayer === 1) {
                    gameState.player1Score++;
                    gameState.player1PocketedBalls.push(this.number);
                  } else {
                    gameState.player2Score++;
                    gameState.player2PocketedBalls.push(this.number);
                  }
                  updateScoreDisplay();
                } else {
                  // 打进错误类型的球，切换玩家
                  gameState.currentPlayer = gameState.currentPlayer === 1 ? 2 : 1;
                  updatePlayerDisplay();
                }
              }
            }
          } else {
            whiteBallInPocket = true;
            // 白球进袋，对手获得自由球
            gameState.canPlaceWhiteBall = true;
            gameState.currentPlayer = gameState.currentPlayer === 1 ? 2 : 1;
            updatePlayerDisplay();
            alert('白球进袋，对手获得自由球！请对手点击桌面任意位置放置白球。');
          }
          if (!this.isCue) {
            ballsPocketedThisTurn.push(this);
          }
          updatePocketedBallsDisplay();
          updateSidePockets();
        }
      }
    };

    // 更新玩家显示
    function updatePlayerDisplay() {
      const playerTurnElement = document.getElementById('player-turn');
      const currentPlayerType = gameState.currentPlayer === 1 ? 
          gameState.player1Type : gameState.player2Type;
      
      let displayText = `玩家 ${gameState.currentPlayer}`;
      if (currentPlayerType) {
          displayText += `：<span class="player-indicator ${currentPlayerType}-color"></span>${currentPlayerType === 'solid' ? '全色球' : '花色球'}`;
      } else {
          displayText += '：开球阶段';
      }
      
      if (gameState.canPlaceWhiteBall) {
          displayText += ' (自由球)';
      }
      
      playerTurnElement.innerHTML = displayText;
    }

    // 更新分数显示
    function updateScoreDisplay() {
      const scoreDisplay = document.getElementById('score');
      scoreDisplay.textContent = `玩家1: ${gameState.player1Score} | 玩家2: ${gameState.player2Score}`;
    }

    // 游戏胜利处理
    function handleGameWin(winningPlayer) {
      const endTime = performance.now() / 1000;
      const elapsedTime = endTime - startTime;
      setTimeout(() => {
        alert(`玩家${winningPlayer}获胜！\n用时：${elapsedTime.toFixed(2)}秒`);
        initGame();
      }, 500);
    }

    // 游戏失败处理
    function handleGameLose(losingPlayer) {
      const winningPlayer = losingPlayer === 1 ? 2 : 1;
      const endTime = performance.now() / 1000;
      const elapsedTime = endTime - startTime;
      setTimeout(() => {
        alert(`玩家${losingPlayer}犯规！玩家${winningPlayer}获胜！\n用时：${elapsedTime.toFixed(2)}秒`);
        initGame();
      }, 500);
    }

    // 初始化游戏
    function initGame() {
      score = 0;
      scoreDisplay.textContent = score;
      timeDisplay.textContent = '0:00';
      balls = [];
      pockets = createPockets();
      whiteBallInPocket = false;
      cueBall = new Ball(canvas.width / 4, canvas.height / 2, 'white', 0, true);
      balls.push(cueBall);
      
      // 定义球的类型和颜色
      var ballTypes = [
        { type: 'solid', color: '#FFD700' },    // 1
        { type: 'solid', color: '#FF0000' },    // 2
        { type: 'solid', color: '#0000FF' },    // 3
        { type: 'solid', color: '#800080' },    // 4
        { type: 'solid', color: '#FFA500' },    // 5
        { type: 'solid', color: '#008000' },    // 6
        { type: 'solid', color: '#800000' },    // 7
        { type: 'black', color: '#000000' },    // 8
        { type: 'striped', color: '#FFD700' },  // 9
        { type: 'striped', color: '#FF0000' },  // 10
        { type: 'striped', color: '#0000FF' },  // 11
        { type: 'striped', color: '#800080' },  // 12
        { type: 'striped', color: '#FFA500' },  // 13
        { type: 'striped', color: '#008000' },  // 14
        { type: 'striped', color: '#800000' }   // 15
      ];
      
      // 三角形排列的球
      var startX = canvas.width * 4.8/7;
      var startY = canvas.height / 2;
      var row = 0;
      var count = 0;
      
      for (var i = 1; i <= 5; i++) {
        for (var j = 0; j < i; j++) {
          var x = startX + row * BALL_RADIUS * 1.8;
          var y = startY - (i - 1) * BALL_RADIUS * 1.8 / 2 + j * BALL_RADIUS * 1.8;
          balls.push(new Ball(x, y, ballTypes[count].color, count + 1, false, ballTypes[count].type));
          count++;
        }
        row++;
      }
      
      gameRunning = true;
      blackBallScoreCondition = false;
      isTimerRunning = false;
      startTime = 0;
      lastTime = 0;
      
      // 重置游戏状态
      gameState.currentPlayer = 1;
      gameState.player1Type = null;
      gameState.player2Type = null;
      gameState.player1Score = 0;
      gameState.player2Score = 0;
      gameState.firstBallType = null;
      gameState.gamePhase = 'open';
      gameState.foul = false;
      gameState.consecutiveHits = 0;
      gameState.player1PocketedBalls = [];
      gameState.player2PocketedBalls = [];
      gameState.canPlaceWhiteBall = false;
      
      updatePlayerDisplay();
      updateScoreDisplay();
      updatePocketedBallsDisplay();
      updateSidePockets();
      
      document.addEventListener('DOMContentLoaded', function () {
        requestAnimationFrame(gameLoop);
      });
    }

    // 重置白球位置
    function resetCueBall() {
      cueBall.x = canvas.width / 4;
      cueBall.y = canvas.height / 2;
      cueBall.vx = 0;
      cueBall.vy = 0;
      cueBall.inPocket = false;
      whiteBallInPocket = false;
    }

    // 碰撞检测
    function checkCollisions() {
        gameState.hasHitAnyBall = false; // 重置击中状态
        gameState.hasHitOwnBall = false; // 重置击中自己球的状态
        
        for (var i = 0; i < balls.length; i++) {
            for (var j = i + 1; j < balls.length; j++) {
                var ball1 = balls[i];
                var ball2 = balls[j];
                
                // 跳过已落袋的球
                if (ball1.inPocket || ball2.inPocket) continue;
                
                // 计算两球之间的距离
                var dx = ball2.x - ball1.x;
                var dy = ball2.y - ball1.y;
                var distance = Math.sqrt(dx * dx + dy * dy);
                
                // 如果两球相撞
                if (distance < ball1.radius + ball2.radius) {
                    gameState.hasHitAnyBall = true; // 标记已击中球
                    
                    // 检查是否击中自己的球
                    if (gameState.gamePhase === 'assigned') {
                        const currentPlayerType = gameState.currentPlayer === 1 ? 
                            gameState.player1Type : gameState.player2Type;
                        
                        if ((ball1.isCue && ball2.ballType === currentPlayerType) ||
                            (ball2.isCue && ball1.ballType === currentPlayerType)) {
                            gameState.hasHitOwnBall = true;
                        }
                    }
                    
                    // 计算碰撞角度
                    var angle = Math.atan2(dy, dx);
                    var sin = Math.sin(angle);
                    var cos = Math.cos(angle);
                    
                    // 转换到碰撞坐标系
                    var x1 = 0;
                    var y1 = 0;
                    var x2 = dx * cos + dy * sin;
                    var y2 = dy * cos - dx * sin;
                    
                    // 转换速度到碰撞坐标系
                    var vx1 = ball1.vx * cos + ball1.vy * sin;
                    var vy1 = ball1.vy * cos - ball1.vx * sin;
                    var vx2 = ball2.vx * cos + ball2.vy * sin;
                    var vy2 = ball2.vy * cos - ball2.vx * sin;
                    
                    // 计算碰撞后的速度
                    var vx1Final = ((ball1.radius - ball2.radius) * vx1 + 2 * ball2.radius * vx2) / (ball1.radius + ball2.radius);
                    var vx2Final = ((ball2.radius - ball1.radius) * vx2 + 2 * ball1.radius * vx1) / (ball1.radius + ball2.radius);
                    
                    // 分离重叠的球
                    var overlap = (ball1.radius + ball2.radius) - distance;
                    ball1.x -= overlap * cos / 2;
                    ball1.y -= overlap * sin / 2;
                    ball2.x += overlap * cos / 2;
                    ball2.y += overlap * sin / 2;
                    
                    // 转换回原坐标系
                    ball1.vx = vx1Final * cos - vy1 * sin;
                    ball1.vy = vy1 * cos + vx1Final * sin;
                    ball2.vx = vx2Final * cos - vy2 * sin;
                    ball2.vy = vy2 * cos + vx2Final * sin;
                }
            }
        }
    }

    // 绘制真实台球杆
    function drawCue() {
      if (cueBall.inPocket || !isMouseInCanvas) return;
      
      var angle = Math.atan2(mouseY - cueBall.y, mouseX - cueBall.x);
      var cueLength = 300; // 增加球杆长度
      var startX = cueBall.x + Math.cos(angle) * (cueBall.radius + 5);
      var startY = cueBall.y + Math.sin(angle) * (cueBall.radius + 5);
      
      ctx.save();
      ctx.translate(startX, startY);
      ctx.rotate(angle);
      
      // 球杆参数
      var tipRadius = 2.5; // 杆头半径
      var tipLength = 12; // 杆头长度
      var ferruleLength = 8; // 金属箍长度
      var shaftMaxWidth = 6; // 杆身最大宽度
      var gripWidth = 12; // 握把宽度
      var gripLength = cueLength * 0.4; // 握把长度
      
      // 绘制杆头 (皮革部分)
      ctx.beginPath();
      ctx.moveTo(0, -tipRadius);
      ctx.lineTo(tipLength, -tipRadius * 0.8);
      ctx.lineTo(tipLength, tipRadius * 0.8);
      ctx.lineTo(0, tipRadius);
      ctx.closePath();
      ctx.fillStyle = '#8B4513';
      ctx.fill();
      
      // 绘制金属箍
      ctx.beginPath();
      ctx.moveTo(tipLength, -tipRadius * 0.8);
      ctx.lineTo(tipLength + ferruleLength, -shaftMaxWidth/2);
      ctx.lineTo(tipLength + ferruleLength, shaftMaxWidth/2);
      ctx.lineTo(tipLength, tipRadius * 0.8);
      ctx.closePath();
      ctx.fillStyle = '#C0C0C0';
      ctx.fill();
      
      // 绘制杆身
      ctx.beginPath();
      ctx.moveTo(tipLength + ferruleLength, -shaftMaxWidth/2);
      ctx.lineTo(cueLength - gripLength, -shaftMaxWidth/2);
      ctx.lineTo(cueLength, -gripWidth/2);
      ctx.lineTo(cueLength, gripWidth/2);
      ctx.lineTo(cueLength - gripLength, shaftMaxWidth/2);
      ctx.lineTo(tipLength + ferruleLength, shaftMaxWidth/2);
      ctx.closePath();
      
      // 创建杆身渐变
      var shaftGradient = ctx.createLinearGradient(0, 0, cueLength, 0);
      shaftGradient.addColorStop(0, '#DEB887');
      shaftGradient.addColorStop(0.5, '#CD853F');
      shaftGradient.addColorStop(1, '#8B4513');
      
      ctx.fillStyle = shaftGradient;
      ctx.fill();
      
      // 添加杆身纹理
      for (var i = tipLength + ferruleLength; i < cueLength - gripLength; i += 6) {
        ctx.beginPath();
        ctx.moveTo(i, -shaftMaxWidth/2 - 1);
        ctx.lineTo(i, shaftMaxWidth/2 + 1);
        ctx.lineWidth = 0.5;
        ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)';
        ctx.stroke();
      }
      
      // 绘制握把
      ctx.beginPath();
      ctx.moveTo(cueLength - gripLength, -shaftMaxWidth/2);
      ctx.lineTo(cueLength, -gripWidth/2);
      ctx.lineTo(cueLength, gripWidth/2);
      ctx.lineTo(cueLength - gripLength, shaftMaxWidth/2);
      ctx.closePath();
      
      var gripGradient = ctx.createLinearGradient(cueLength - gripLength, 0, cueLength, 0);
      gripGradient.addColorStop(0, '#333333');
      gripGradient.addColorStop(1, '#000000');
      
      ctx.fillStyle = gripGradient;
      ctx.fill();
      
      // 添加握把纹理
      for (var i = cueLength - gripLength; i < cueLength; i += 4) {
        ctx.beginPath();
        ctx.moveTo(i, -gripWidth/2);
        ctx.lineTo(i, gripWidth/2);
        ctx.lineWidth = 0.5;
        ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
        ctx.stroke();
      }
      
      ctx.restore();
    }
    
    // 绘制桌球台
    function drawTable() {
      // 创建渐变背景
      const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
      gradient.addColorStop(0, '#0b6c40');
      gradient.addColorStop(1, '#063822');
      ctx.fillStyle = gradient;
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      
      // 绘制边框（添加光泽效果）
      ctx.strokeStyle = '#654321';
      ctx.lineWidth = 22;
      ctx.strokeRect(0, 0, canvas.width, canvas.height);
      
      // 添加边框光泽
      ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
      ctx.lineWidth = 2;
      ctx.strokeRect(11, 11, canvas.width - 22, canvas.height - 22);
      
      // 绘制球袋
      ctx.fillStyle = 'black';
      for (var i = 0; i < pockets.length; i++) {
        var pocket = pockets[i];
        ctx.beginPath();
        ctx.arc(pocket.x, pocket.y, POCKET_RADIUS, 0, Math.PI * 2);
        ctx.fill();
      }
      
      // 绘制中间线
      ctx.strokeStyle = 'white';
      ctx.lineWidth = 1.1; // 原1px ×1.1
      ctx.setLineDash([5.5, 5.5]); // 原5px ×1.1
      ctx.beginPath();
      ctx.moveTo(canvas.width / 4, 0);
      ctx.lineTo(canvas.width / 4, canvas.height);
      ctx.stroke();
      ctx.setLineDash([]);
      
      // 绘制中间点
      ctx.beginPath();
      ctx.arc(canvas.width / 4, canvas.height / 2, 3.3, 0, Math.PI * 2); // 原3px ×1.1
      ctx.fillStyle = 'white';
      ctx.fill();
      
      // 绘制球型号码位置标记
      ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
      var markerRadius = 2;
      var startX = canvas.width * 4.8/7;
      var startY = canvas.height / 2;
      var row = 0;
      
      for (var i = 1; i <= 5; i++) {
        for (var j = 0; j < i; j++) {
          var x = startX + row * BALL_RADIUS * 1.8;
          var y = startY - (i - 1) * BALL_RADIUS * 1.8 / 2 + j * BALL_RADIUS * 1.8;
          ctx.beginPath();
          ctx.arc(x, y, markerRadius, 0, Math.PI * 2);
          ctx.fill();
        }
        row++;
      }
    }

    // 辅助函数
    function allBallsStopped() {
      for (var i = 0; i < balls.length; i++) {
        var ball = balls[i];
        if (!ball.inPocket && (Math.abs(ball.vx) > MIN_VELOCITY || Math.abs(ball.vy) > MIN_VELOCITY)) {
          return false;
        }
      }
      return true;
    }

    // 主循环
    function gameLoop(timestamp) {
        if (!gameRunning) return;
        
        var deltaTime = timestamp - lastTime;
        lastTime = timestamp;
        
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        drawTable();
        
        for (var i = 0; i < balls.length; i++) {
            balls[i].update();
        }
        
        // 只有当白球可以放置时才跳过碰撞检测和后续逻辑
        if (!gameState.canPlaceWhiteBall) {
             checkCollisions();
        }
       
        for (var i = 0; i < balls.length; i++) {
            balls[i].draw();
        }
        
        // 在所有球停止且白球不在袋中，并且不是自由球放置状态时，绘制球杆并处理回合逻辑
        if (allBallsStopped() && !whiteBallInPocket && !gameState.canPlaceWhiteBall) {
            drawCue();
            
            if (!gameStarted) {
                gameStarted = true;
            }
            
            // 处理开球阶段和分配类型阶段
            if (gameState.gamePhase === 'open') {
                if (ballsPocketedThisTurn.length === 0) {
                    // 在开球阶段，如果没有球进袋
                    if (!gameState.hasHitAnyBall) {
                        // 如果未击中任何球，切换玩家
                        gameState.currentPlayer = gameState.currentPlayer === 1 ? 2 : 1;
                        updatePlayerDisplay();
                    } else {
                        // 如果击中了球但没进袋，也切换玩家
                        gameState.currentPlayer = gameState.currentPlayer === 1 ? 2 : 1;
                        updatePlayerDisplay();
                    }
                    // 清空本轮进袋记录和击中状态
                    ballsPocketedThisTurn = [];
                    gameState.hasHitAnyBall = false;
                } else {
                    // 有球进袋，进入分配类型阶段
                    gameState.firstBallType = ballsPocketedThisTurn[0].ballType;
                    gameState.player1Type = gameState.firstBallType;
                    gameState.player2Type = gameState.firstBallType === 'solid' ? 'striped' : 'solid';
                    gameState.gamePhase = 'assigned';
                    // 记录第一个进球的玩家
                    if (gameState.currentPlayer === 1) {
                         gameState.player1PocketedBalls.push(ballsPocketedThisTurn[0].number);
                    } else {
                         gameState.player2PocketedBalls.push(ballsPocketedThisTurn[0].number);
                    }
                   
                    updatePlayerDisplay();
                    ballsPocketedThisTurn = [];
                }
            } else if (gameState.gamePhase === 'assigned') {
                // 分配类型阶段
                if (!gameState.hasHitOwnBall && gameState.hasHitAnyBall) {
                    // 犯规：击中了球但不是自己的球
                    gameState.foul = true; // 标记犯规
                    gameState.canPlaceWhiteBall = true; // 进入自由球状态
                    // 切换玩家在放置白球后进行
                    updatePlayerDisplay();
                    initGame();
                    
                } else if (ballsPocketedThisTurn.length === 0) {
                    // 没有进球且没有犯规，切换玩家
                     gameState.currentPlayer = gameState.currentPlayer === 1 ? 2 : 1;
                     updatePlayerDisplay();
                } else {
                   // 有进球且不是犯规，当前玩家继续击球
                   // 进球列表和分数已在Ball.update中更新
                }
                
                // 清空本轮状态，留待下一击开始前重置
                ballsPocketedThisTurn = [];
                gameState.hasHitAnyBall = false;
                gameState.hasHitOwnBall = false;
                gameState.foul = false; // 清除犯规标记
            }
        }
        
        // 更新蓄力条
        if (isCharging) {
            chargePower = Math.min(chargePower + deltaTime / 30, 100);
            powerLevel.style.width = chargePower + '%';
            
            // 更新蓄力提示
            var tooltip = document.getElementById('power-tooltip');
            tooltip.querySelector('span').textContent = Math.round(chargePower) + '%';
            tooltip.style.opacity = '1';
            tooltip.style.left = (mouseX + 20) + 'px';
            tooltip.style.top = (mouseY - 30) + 'px';
        } else {
            document.getElementById('power-tooltip').style.opacity = '0';
        }
        
        // 启动计时器
        if (gameRunning && !isTimerRunning) {
            startTime = performance.now() / 1000;
            isTimerRunning = true;
        }
        
        // 更新计时器显示
        if (isTimerRunning) {
            var currentTime = performance.now() / 1000 - startTime;
            var minutes = Math.floor(currentTime / 60);
            var seconds = Math.floor(currentTime % 60);
            var formattedTime = (minutes < 10? '0' : '') + minutes + ':' + (seconds < 10? '0' : '') + seconds;
            timeDisplay.textContent = formattedTime;
        }
        
        requestAnimationFrame(gameLoop);
    }

    // 添加显示已进球的功能
    function updatePocketedBallsDisplay() {
        // 移除旧的显示
        const oldDisplay = document.getElementById('pocketed-balls-display');
        if (oldDisplay) oldDisplay.remove();
    }

    // 修改鼠标事件处理
    canvas.addEventListener('mousedown', function(e) {
        // 只在所有球停止时允许操作
        if (allBallsStopped()) {
            var rect = canvas.getBoundingClientRect();
            var x = e.clientX - rect.left;
            var y = e.clientY - rect.top;

            if (gameState.canPlaceWhiteBall) {
                // 自由球状态，可以放置白球
                
                // 确保白球不会放在袋口或与其他球重叠
                var validPlacement = true;
                for (var i = 0; i < pockets.length; i++) {
                    var pocket = pockets[i];
                    var dx = x - pocket.x;
                    var dy = y - pocket.y;
                    var distance = Math.sqrt(dx * dx + dy * dy);
                    if (distance < POCKET_RADIUS * 2) {
                        validPlacement = false;
                        break;
                    }
                }
                // 检查与其他球的重叠
                 if (validPlacement) {
                     for (var i = 0; i < balls.length; i++) {
                          const ball = balls[i];
                          if (!ball.inPocket) { // 只检查未进袋的球
                               var dx = x - ball.x;
                               var dy = y - ball.y;
                               var distance = Math.sqrt(dx * dx + dy * dy);
                               if (distance < cueBall.radius * 2) { // 如果距离小于两个球半径之和，则重叠
                                    validPlacement = false;
                                    break;
                                }
                           }
                     }
                 }
                
                if (validPlacement) {
                    cueBall.x = x;
                    cueBall.y = y;
                    cueBall.vx = 0; // 确保白球静止
                    cueBall.vy = 0;
                    cueBall.inPocket = false;
                    whiteBallInPocket = false;
                    gameState.canPlaceWhiteBall = false; // 退出自由球状态
                    // 在放置白球后切换玩家
                    gameState.currentPlayer = gameState.currentPlayer === 1 ? 2 : 1;
                    updatePlayerDisplay();
                    // 清空本轮状态
                    ballsPocketedThisTurn = [];
                    gameState.hasHitAnyBall = false;
                    gameState.hasHitOwnBall = false;
                    gameState.foul = false;

                } else {
                    alert('不能将白球放在袋口或与其他球重叠的位置！');
                }
            } else if (!whiteBallInPocket) {
                // 非自由球状态，可以蓄力击球
                isCharging = true;
                chargePower = 0;
            }
        }
    });

    canvas.addEventListener('mouseup', function(e) {
      if (isCharging) {
        isCharging = false;
        
        var rect = canvas.getBoundingClientRect();
        mouseX = e.clientX - rect.left;
        mouseY = e.clientY - rect.top;
        
        var angle = Math.atan2(mouseY - cueBall.y, mouseX - cueBall.x);
        var power = chargePower / 4;
        
        // 设置白球速度
        cueBall.vx = -Math.cos(angle) * power;
        cueBall.vy = -Math.sin(angle) * power;
        
        // 重置蓄力条
        powerLevel.style.width = '0%';
      }
    });

    canvas.addEventListener('mousemove', function(e) {
      var rect = canvas.getBoundingClientRect();
      mouseX = e.clientX - rect.left;
      mouseY = e.clientY - rect.top;
      
      isMouseInCanvas = 
        mouseX >= 0 && mouseX <= canvas.width && 
        mouseY >= 0 && mouseY <= canvas.height;
    });

    canvas.addEventListener('mouseleave', function() {
      isMouseInCanvas = false;
      document.getElementById('power-tooltip').style.opacity = '0';
    });

    canvas.addEventListener('mouseenter', function() {
      isMouseInCanvas = true;
    });

    // 新增：更新左右方框显示玩家进球
    function updateSidePockets() {
        // 玩家1
        const p1 = document.getElementById('player1-pocket');
        p1.innerHTML = `<h3>玩家1 ${gameState.player1Type === 'solid' ? '全色球' : '花色球'}</h3><div class='ball-list'>${gameState.player1PocketedBalls.map(num => {
            const ball = balls.find(b => b.number === num);
            return `<div class='ball-item' style='background-color:${ball.color}'>
                <div class='ball-inner'>
                    ${ball.ballType === 'striped' ? `
                        <div class='ball-stripe'></div>
                    ` : ''}
                    <div class='ball-number'>${num}</div>
                </div>
            </div>`;
        }).join('')}</div>`;
        
        // 玩家2
        const p2 = document.getElementById('player2-pocket');
        p2.innerHTML = `<h3>玩家2 ${gameState.player2Type === 'solid' ? '全色球' : '花色球'}</h3><div class='ball-list'>${gameState.player2PocketedBalls.map(num => {
            const ball = balls.find(b => b.number === num);
            return `<div class='ball-item' style='background-color:${ball.color}'>
                <div class='ball-inner'>
                    ${ball.ballType === 'striped' ? `
                        <div class='ball-stripe'></div>
                    ` : ''}
                    <div class='ball-number'>${num}</div>
                </div>
            </div>`;
        }).join('')}</div>`;
    }

    // 添加新的样式
    const style = document.createElement('style');
    style.textContent = `
        .ball-item {
            width: 28px;
            height: 28px;
            border-radius: 50%;
            position: relative;
            overflow: hidden;
            box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.3);
            border: 1px solid rgba(255, 255, 255, 0.2);
        }
        
        .ball-inner {
            width: 100%;
            height: 100%;
            position: relative;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        
        .ball-stripe {
            position: absolute;
            width: 100%;
            height: 30%; /* 调整条纹高度 */
            background-color: white;
            top: 35%; /* 调整条纹垂直位置 */
            left: 0;
        }
        
        .ball-number {
            width: 40%; /* 调整数字背景大小 */
            height: 40%; /* 调整数字背景大小 */
            background-color: white;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 12px;
            font-weight: bold;
            color: black;
            border: 1px solid rgba(0, 0, 0, 0.3);
            z-index: 1;
        }
    `;
    document.head.appendChild(style);

    // 开始游戏
    initGame();
  </script>
<!-- Code injected by live-server -->
<script>
	// <![CDATA[  <-- For SVG support
	if ('WebSocket' in window) {
		(function () {
			function refreshCSS() {
				var sheets = [].slice.call(document.getElementsByTagName("link"));
				var head = document.getElementsByTagName("head")[0];
				for (var i = 0; i < sheets.length; ++i) {
					var elem = sheets[i];
					var parent = elem.parentElement || head;
					parent.removeChild(elem);
					var rel = elem.rel;
					if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") {
						var url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, '');
						elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());
					}
					parent.appendChild(elem);
				}
			}
			var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';
			var address = protocol + window.location.host + window.location.pathname + '/ws';
			var socket = new WebSocket(address);
			socket.onmessage = function (msg) {
				if (msg.data == 'reload') window.location.reload();
				else if (msg.data == 'refreshcss') refreshCSS();
			};
			if (sessionStorage && !sessionStorage.getItem('IsThisFirstTime_Log_From_LiveServer')) {
				console.log('Live reload enabled.');
				sessionStorage.setItem('IsThisFirstTime_Log_From_LiveServer', true);
			}
		})();
	}
	else {
		console.error('Upgrade your browser. This Browser is NOT supported WebSocket for Live-Reloading.');
	}
	// ]]>
</script>
</body>
</html>    